
# -------------------------------------------------------------------------------------------
$FileHandlerClass = New-PSClass FileHandler {
	note Pattern
	note Execution

	method GetHandler {
		$HandlerName = $Args[0]
		$DBDeployProvider = $Args[1]
		
		$this.$HandlerName
	}
}

# -------------------------------------------------------------------------------------------
$TargetEnvironmentsClass = New-PSClass TargetEnvironments {
	note default
	note TargetEnvironment

	method GetTargetEnvironment {
		$EnvironmentName = $Args[0]
		$env = $this.TargetEnvironment | ?{$_.Name -eq $EnvironmentName}
		if ($env -eq $null) {Throw "Target Environment '$EnvironmentName' not Found in configuration"}
		if ($env.count -gt 1) {Throw "Target Environment '$EnvironmentName' not Unique in configuration"}
		$env
	}
}

# -------------------------------------------------------------------------------------------
$FileHandlersClass = New-PSClass FileHandlers {
	note FileHandler
	note ProcessAllOnError

	Constructor {
		$FileHandlerClass.AttachTo($this.FileHandler)
	}

	method FileHandlerForFile {
		$filename = $Args[0]
		foreach ($FileHandler in $this.FileHandler)
		{
			if ($filename -match $FileHandler.pattern)
			{
				return $FileHandler
			}
		}
		Throw "No Handler for '$filename'"
	}
}

# -------------------------------------------------------------------------------------------
$PatchGroupClass = New-PSClass PatchGroup {

	note Name 
	note Path
	note Pattern
	note ExecuteOnce
	note FileHandlers
	note DefaultExecute 
	note MetaData
	
	note DBFrameworkConfig
	
	note -private ProjectDir
	
	note PatchFiles @()
	
	constructor {
		$this.DBFrameworkConfig = $Args[0]
		$this.Path = $this.Path -replace '^\\?(.*?)\\*$', '$1'
		$FileHandlersClass.AttachTo($this.FileHandlers)
	}

	property ProjectDir {
		return $Private.ProjectDir
	} -set {
		if (! (Test-Path $Args[0]) ) 
		{
			Throw "Project Path $($Args[0]) does not exits"
		}
		$Private.ProjectDir =  $Args[0]
	}
	
	property Patchdir {
		$result = Join-Path $this.ProjectDir $this.Path
		return $result
	}
	
	method Process {
		$DBDeployProvider = $Args[0]
		
		$ExecuteCondition = $TRUE
		
		if (([string]$this.DefaultExecute) -eq "False")
		{
			$ExecuteCondition = $FALSE
			#$ExecuteCondition = $ConditionSwitches -contains $this.ConditionSwitch
			#dv $ExecuteCondition ExecuteCondition
		}
		
		$ExecutePatchGroup = ($PatchGroups -contains "All" -and $ExecuteCondition) -or ($PatchGroups -contains $this.name)
		if (($ExecutePatchGroup -eq $TRUE))
		{
		
			$this.AttachFileMetaData()
			$this.SortExecutionList()

			$PatchSequencer = $PatchSequencerClass.New($DBDeployProvider)

			foreach ($manifestFile in $this.PatchFiles)
			{
				if ($DBDeployProvider.ShouldPatchExecute($manifestFile.Path,$manifestFile.SourceCheckSum)  -or $DBDeployProvider.force)
				{
					$FileHandler = $this.FileHandlers.FileHandlerForFile($manifestFile.Path)
					$patchMatch = $DBDeployProvider.PatchMatchForManifest($manifestFile,$FileHandler,$this)
					$PatchSequencer.Add($FileHandler, $patchMatch)
				}
			}

			$PatchSequencer.Process()
		}
		else
		{
			Log-Message "Skipping PatchGroup $($this.name)"
		}
	}
	
	method AttachFileMetaData {
		if ($this.MetaData)
		{
			foreach ($fileMetaData in $this.MetaData.FileMetaData)
			{
				$manifestFile = $this.PatchFiles | ?{$_.Path -eq $fileMetaData.name}
				if ($manifestFile -eq $null)
				{
					Log-Warning "FileMetaData '$($fileMetaData.name)' does not exist or already executed." 
				}
				else
				{
					Log-Message -Level Verbose "Attaching FileMetaData '$($fileMetaData.name)'" 
					$manifestFile.MetaData = $fileMetaData
				}
			}
		}
	}
	
	method SortExecutionList {
	  # Move $this.PatchFiles into ArrayList - Add back with Dependencies Resolved
		$ManifestAL = New-Object System.Collections.ArrayList
		$ManifestAL.AddRange($this.PatchFiles)
		
		$this.PatchFiles = @()
		
		Log-Message -Level Debug "Sorting Dependencies"
		
		while ($ManifestAL.Count -gt 0)
		{
			$SomethingAdded = $FALSE
			
			$idx = 0;
			while ($idx -lt $ManifestAL.Count)
			{
				$manifestFile = $ManifestAL[$idx]
				$OkayToAdd = $TRUE
				if( $manifestFile.MetaData.Dependencies -ne $null )
				{
					foreach($dependency in ($manifestFile.MetaData.Dependencies.Dependency))
					{
						if ( ($this.PatchFiles  | ?{$_.Path -eq $dependency.Name}) -eq $null)
						{
							$OkayToAdd = $FALSE
							Log-Message -Level Debug "$($manifestFile.Path) not ready because of dependency $($dependency.Name)"
						}
					}
				}
				if ($OkayToAdd)
				{
					$this.PatchFiles  += $manifestFile
					$ManifestAL.RemoveAt($idx)
					$SomethingAdded = $TRUE
					Log-Message -Level Debug "Added $($manifestFile.Path)"
				}
				else
				{
					Log-Message -Level Debug "Skipped $($manifestFile.Path)"
					$idx++
				}
			}
			if (-not $SomethingAdded)
			{
				Log-Error "Dependencies on Files are confused - Remaining Files are:"
				foreach ($manifestFile in $ManifestAL)
				{
					Log-Error "    $($manifestFile.Path)"
				}
				Throw "Error in File dependencies"
			}
		}
	}

}

$ConfigScriptsClass = New-PSClass ConfigScripts {
	note Script
	
	method GetScript {
		$ScriptName = $Args[0]
		foreach ($script in $this.script)
		{
			if ($script.name -eq $ScriptName)
			{
				return $script
			}
		}
	}
}

# -------------------------------------------------------------------------------------------
$DBFrameworkConfigClass = New-PSClass DBFrameworkConfig {
  note RootDirectory
	note Provider
	
	note PatchGroups
	note Scripts
	note TargetEnvironments
	
	Constructor {
		[void] $PatchGroupClass.AttachTo($this.PatchGroups.PatchGroup, $this)
		[void] $ConfigScriptsClass.AttachTo($this.Scripts)
		[void] $TargetEnvironmentsClass.AttachTo($this.TargetEnvironments)
	}

	method -private CleanPath {
		$Args[0] -replace '^\\?(.*?)\\*$', '$1'
	}
	
	method PatchGroupForManifest {
		$Manifest = $Args[0]
		
		$file = [system.IO.FileInfo] $Manifest.Path
		$fileName = ([system.IO.FileInfo] $file).name
		$filepath = [System.IO.Path]::GetDirectoryName( $Manifest.Path ) -replace '^\\?(.*?)\\*$', '$1'

		foreach ($PatchGroup in $this.PatchGroups.PatchGroup)
		{
			if ( ($filepath.StartsWith($PatchGroup.Path)) -and ($fileName -match $PatchGroup.Pattern) )
			{
				return $PatchGroup
			}
		}
	}
}
# -------------------------------------------------------------------------------------------
